package ualrcdg.shared.dto;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
 * Static description of a Finite State Machine with lookups implemented using a transition map.
 * Transitions are recorded (and hence retrieved) in the following manner:  
 *   (StateType) objects map to "transition rows", each of which is a further mapping from (TransitionType) objects to (EntryType) objects
 *   note: EntryType is typically either the same as StateType (for DFAs), or is instead a generic collection with StateType as the type parameter (for NFAs)
 * 
 * @param <StateType> data type used to represent a state in the represented FSM
 * @param <TransitionType> data type used to represent a transition in the represented FSM
 * @param <EntryType> data type used to represent entries in the represented FSM's transition map 
 */
public class FSM_TransitionMap< StateType, TransitionType, EntryType >
{
	protected final StateType initialState;
	protected final StateType[] acceptingStates;
	protected final HashMap< StateType, HashMap< TransitionType, EntryType > > transitionMap;
	
	/**
	 * Primary constructor
	 *
	 * @param initialState the initialState of the FSM_TransitionMap object to be created
	 * @param acceptingStates the acceptingStates of the FSM_TransitionMap object to be created
	 * @param transitionMap the transitionMap of the FSM_TransitionMap object to be created
	 */
	public FSM_TransitionMap( StateType initialState, StateType[ ] acceptingStates, HashMap< StateType, HashMap< TransitionType, EntryType > > transitionMap )
	{
		this.initialState = initialState;
		this.acceptingStates = Arrays.copyOf( acceptingStates, acceptingStates.length );
		this.transitionMap = new HashMap< StateType, HashMap<TransitionType,EntryType> >( );
		for( Map.Entry< StateType, HashMap<TransitionType,EntryType> > e1 : transitionMap.entrySet( ) )
		{
			HashMap<TransitionType,EntryType> e1Map = e1.getValue( );
			HashMap<TransitionType,EntryType> e1MapCopy = new HashMap< TransitionType, EntryType >( );
			
			for( Map.Entry< TransitionType, EntryType > e2 : e1Map.entrySet( ) )
			{
				e1MapCopy.put( e2.getKey( ), e2.getValue( ) );
			}
			
			this.transitionMap.put( e1.getKey( ), e1MapCopy );
		}
	}

	/*
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString( )
	{
		return "FSM_TransitionMap [initialState=" + initialState + ", acceptingStates=" + Arrays.toString( acceptingStates ) + ", transitionMap=" + transitionMap.toString( ) + "]";
	}

	/**
	 * @return the initialState of this FSM_TransitionMap instance
	 */
	public StateType getInitialState( )
	{
		return initialState;
	}
	
	/**
	 * Checks if a given state is an accepting state on this FSM
	 * 
	 * @param currentState the state identifier to check
	 * @return true if the currentState is an accepting state of this FSM_TransitionMap instance, false otherwise
	 */
	public boolean isAcceptingState( StateType currentState )
	{
		for( StateType i : acceptingStates )
		{
			if( i.equals( currentState ) )
			{
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Fetches the destination state given a current state and a transition to be made
	 * 
	 * @param currentState the state from which the transition should be made
	 * @param transition the transition to make from the currentState
	 * @return the resultant destination state, or null if no such transition exists
	 */
	public EntryType getTransition( StateType currentState, TransitionType transition )
	{
		HashMap< TransitionType, EntryType > entries = transitionMap.get( currentState );
		if( entries == null )
		{
			return null;
		}
		
		return entries.get( transition );
	}
}
